package com.ak.game.netty;

import cn.hutool.core.util.ObjectUtil;
import com.ak.game.entity.PnGameTwistRecord;
import com.ak.game.entity.vo.LoginReqVo;
import com.ak.game.exception.ServiceException;
import com.ak.game.service.*;
import com.ak.game.util.AjaxResult;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker;
import io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory;
import io.netty.util.AttributeKey;
import io.netty.util.internal.StringUtil;
import lombok.extern.slf4j.Slf4j;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * websocket长连接权限验证的handler代理类
 */
@Slf4j
public class WebSocketAuthHandler extends ChannelInboundHandlerAdapter {

    /**
     * pn_game表的service层
     */
    private final PnGameService gameService;

    private final String websocketPath;

    private final String gameId;

    private final RemoteWebSocketClientService clientService;

    private final PnUserStreamService userStreamService;


    private final UserMoneySessionService userMoneySessionService;

    private final PnGameTwistRecordService gameTwistRecordService;

    public WebSocketAuthHandler(PnGameService gameService, String websocketPath, RemoteWebSocketClientService service, String gameId, PnUserStreamService userStreamService, UserMoneySessionService userMoneySessionService, PnGameTwistRecordService gameTwistRecordService) {
        this.websocketPath = websocketPath;
        this.gameService = gameService;
        this.clientService = service;
        this.gameId = gameId;
        this.userStreamService = userStreamService;
        this.userMoneySessionService = userMoneySessionService;
        this.gameTwistRecordService = gameTwistRecordService;
    }


    /**
     * 处理请求
     *
     * @param ctx 通道代理上下文
     * @param msg 接收到的bytebuffer类型的消息
     * @throws Exception 异常
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (msg instanceof FullHttpRequest) {
            FullHttpRequest request = (FullHttpRequest) msg;
            handleHttpRequest(ctx, request);
        } else {
            DefaultFullHttpResponse defaultFullHttpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST);
            ctx.channel().writeAndFlush(defaultFullHttpResponse);
        }

    }

    /**
     * FullHttpRequest请求具体的处理方法
     *
     * @param ctx 通道代理上下文
     * @param req Http请求体
     */
    private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) {
        // 检查是否为WebSocket握手请求
        if (!req.decoderResult().isSuccess() || !req.method().equals(HttpMethod.GET) || !req.uri().startsWith(websocketPath)) {
            sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));
            return;
        }
        // 验签逻辑 1.验签 2.验证用户登录状态
        boolean login = isLogin(req, ctx);
        boolean validSignature = isValidSignature(req);
        if (login && validSignature) {
            WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(getWebSocketLocation(req), null, false);
            WebSocketServerHandshaker handshaker = wsFactory.newHandshaker(req);
            if (handshaker == null) {
                WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
            } else {
                handshaker.handshake(ctx.channel(), req);
                QueryStringDecoder decoder = new QueryStringDecoder(req.uri());
                Map<String, List<String>> params = decoder.parameters();
                ctx.channel().attr(AttributeKey.valueOf("params")).set(params);
                ctx.pipeline().remove(WebSocketAuthHandler.class);
                // 查询用户未结算的宝石下注记录
                String userId = params.get("user_id").get(0);
                LambdaQueryWrapper<PnGameTwistRecord> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.eq(PnGameTwistRecord::getUserId,userId);
                queryWrapper.eq(PnGameTwistRecord::getGameId,gameId);
                queryWrapper.eq(PnGameTwistRecord::getIsOver,"1");
                List<PnGameTwistRecord> pnGameTwistRecords = gameTwistRecordService.getBaseMapper().selectList(queryWrapper);
                Map<String,BigDecimal> map = new HashMap<>();
                for (PnGameTwistRecord pnGameTwistRecord : pnGameTwistRecords) {
                    // 未结算的下注记录1-绿色宝石 2-红色宝石 3-蓝色宝石
                    String sysSelect = pnGameTwistRecord.getSysSelect();
                    map.put(String.valueOf(sysSelect),pnGameTwistRecord.getMultiple());
                }
                ctx.channel().writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(AjaxResult.success(map,"1000"))));
                BettingService.USER_CTX_MAP.put(userId,ctx);

            }
        } else {
            sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.UNAUTHORIZED));
        }
    }

    /**
     * 验证用户登录状态
     *
     * @param request Http请求体
     */
    private boolean isLogin(FullHttpRequest request, ChannelHandlerContext ctx) {
        // 提取URL参数
        QueryStringDecoder decoder = new QueryStringDecoder(request.uri());
        Map<String, List<String>> params = decoder.parameters();
        String token = "";
        String userId = "";
        if (ObjectUtil.isNotEmpty(params.get("user_id")) && ObjectUtil.isNotEmpty(params.get("token"))) {
            userId = params.get("user_id").get(0);
            token = params.get("token").get(0);
            LoginReqVo loginReqVo = new LoginReqVo().setData(Map.of("token", token, "user_id", userId)).setGame(Map.of("id", gameId)).setMsg_id("106");
            try {
                ctx.channel().attr(AttributeKey.valueOf("userId")).set(userId);
                CompletableFuture<Object> booleanCompletableFuture = clientService.sendMessage(JSON.toJSONString(loginReqVo));
                Object isLogin = booleanCompletableFuture.get(5, TimeUnit.SECONDS);
                return Boolean.parseBoolean(isLogin.toString());
            } catch (InterruptedException | ExecutionException | TimeoutException e) {
                RemoteWebSocketClientService.FUTURES.remove(userId);
                return false;
            }
        } else {
            return false;
        }
    }

    /**
     * 验签方法
     *
     * @param request Http请求体
     */
    private boolean isValidSignature(FullHttpRequest request) {
        QueryStringDecoder decoder = new QueryStringDecoder(request.uri());
        Map<String, List<String>> params = decoder.parameters();
        String sign = params.get("sign").get(0);
        String gameId = params.get("game_id").get(0);
        String timestamp = params.get("timestamp").get(0);
        // 取出header参数：sign、game_id、timestamp
        log.info("收到连接请求，sign={}", sign);
        log.info("收到连接请求，game_id={}", gameId);
        log.info("收到连接请求，timestamp={}", timestamp);
        if (StringUtil.isNullOrEmpty(sign) || StringUtil.isNullOrEmpty(gameId) || StringUtil.isNullOrEmpty(timestamp)) {
            throw new ServiceException("参数不全");
        }
        // 验签
        return gameService.validSignature(sign, gameId, Long.parseLong(timestamp));
    }

    /**
     * FullHttpResponse返回方法
     *
     * @param ctx 通道代理上下文
     * @param req Http请求体
     * @param res Http返回体
     */
    private void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest req, DefaultFullHttpResponse res) {
        if (!HttpUtil.isKeepAlive(req) || res.status().code() != 200) {
            res.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE);
        } else {
            res.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
        }
        ChannelFuture f = ctx.channel().writeAndFlush(res);
        if (!HttpUtil.isKeepAlive(req)) {
            f.addListener(ChannelFutureListener.CLOSE);
        }
    }

    /**
     * 获取连接请求全URL
     *
     * @param req Http请求体
     */
    private String getWebSocketLocation(FullHttpRequest req) {
        String location = req.headers().get(HttpHeaderNames.HOST) + req.uri();
        return "ws://" + location;
    }

    /**
     * 管道异常处理，防止资源泄露
     *
     * @param ctx   通道代理上下文
     * @param cause 异常
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        log.error("连接失败：{}", cause.getMessage()); // 记录日志
        String userId = ctx.channel().attr(AttributeKey.valueOf("userId")).get().toString();
        RemoteWebSocketClientService.FUTURES.remove(userId);
        ctx.close(); // 关闭发生异常的通道，防止资源泄露
    }
}
